package com.quectel.grid.controller.grid;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import com.aliyuncs.utils.StringUtils;
import com.quectel.business.common.grid.GridEventRecordBusiness;
import com.quectel.business.common.grid.GridMemberBindResourcesBusiness;
import com.quectel.constant.OrderCodePrefixConstants;
import com.quectel.constant.core.grid.GridConstant;
import com.quectel.constant.core.grid.GridPlanTaskConstant;
import com.quectel.constant.global.SqlConstants;
import com.quectel.constant.global.StatisticsConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.core.module.grid.dto.*;
import com.quectel.core.module.grid.service.GridEventRecordLogService;
import com.quectel.core.module.grid.service.GridEventRecordService;
import com.quectel.core.module.grid.service.GridPlanTaskService;
import com.quectel.core.module.grid.service.GridService;
import com.quectel.core.module.grid.vo.TaskTrendStatisticsVo;
import com.quectel.core.module.village.service.VillageService;
import com.quectel.grid.controller.BaseController;
import com.quectel.grid.interceptor.LoginGridMemberSession;
import com.quectel.grid.util.SessionHolder;
import com.quectel.grid.vo.grid.ByTimeCountResp;
import com.quectel.grid.vo.grid.GridPlanTaskDetailResp;
import com.quectel.grid.vo.grid.GridPlanTaskStatisticsVo;
import com.quectel.grid.vo.grid.GridPlanTaskVo;
import com.quectel.util.common.*;
import com.quectel.util.kit.OrderUtils;
import com.quectel.util.redis.RedisUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.quectel.constant.global.StatisticsConstants.TIME_RANGE_BY_DAY;
import static com.quectel.constant.global.StatisticsConstants.TIME_RANGE_BY_MONTH;

/**
 * @author bob
 * @email bob.yu@quectel.com
 * @date 2022/5/24 10:58
 */
@RestController
@RequestMapping("gridPlanTask")
@Tag(name = "GridPlanTaskController", description = "网格巡检相关的api")
public class GridPlanTaskController extends BaseController {

    /**
     * 上报限制,根据任务id
     */
    private static final String GRID_TASK_REPORT_LIMIT_LOCK_BY_TASKID = "GRID_TASK_REPORT_LIMIT_LOCK_BY_TASKID:";
    /**
     * 上报间隔
     */
    private static final int GRID_TASK_REPORT_TIME_LIMIT = 2;

    private static final String EXEC_START_TIME = "execStartTime";
    private static final String EXEC_END_TIME = "execEndTime";
    private static final String EXEC_TIME = "execTime";
    private static final String NOT_NULL = "notNull";

    @DubboReference
    private GridPlanTaskService gridPlanTaskService;
    @DubboReference
    private GridEventRecordService gridEventRecordService;
    @DubboReference
    private GridEventRecordLogService gridEventRecordLogService;
    @DubboReference
    private VillageService villageService;
    @DubboReference
    private GridService gridService;

    @Autowired
    private GridMemberBindResourcesBusiness resourcesBusiness;
    @Autowired
    private GridEventRecordBusiness gridEventRecordBusiness;

    /**
     * 网格巡查任务统计
     * 返回巡查总时长 + 巡查上报事件数量
     */
    @GetMapping("gridTaskStatistics")
    @Operation(summary = "网格巡查任务统计")
    public Response<GridPlanTaskStatisticsVo> gridTaskStatistics(@Parameter(description = "yyyy-MM") @RequestParam String timeString,
                                                                 @Parameter(description = "网格id") @RequestParam(required = false) Long gridId) {
        LoginGridMemberSession loginGridUserSession = SessionHolder.getLoginGridUserSession();
        assert loginGridUserSession != null;
        GridMemberDto gridMember = loginGridUserSession.getGridMember();
        GridPlanTaskStatisticsVo vo = new GridPlanTaskStatisticsVo();

        DateTime parse = DateUtil.parse(timeString, DateUtils.FormatType.MONTH.getType());
        Map<String, Object> queryMap = new HashMap<>();
        queryMap.put("startTime", DateUtil.format(DateUtil.beginOfMonth(parse), DateUtils.FormatType.SIMPLE.getType()) + " 00:00:00");
        queryMap.put("endTime", DateUtil.format(DateUtil.endOfMonth(parse), DateUtils.FormatType.SIMPLE.getType()) + " 23:59:59");

        // 没选中网格 那么传自己拥有的网格的
        if (gridId != null) {
            queryMap.put(LambdaUtil.getFieldName(GridPlanTaskDto::getGridId), gridId);
        } else {
            // 网格员是否拥有网格信息
            List<Long> gridIds = resourcesBusiness.getGridIdByGridMember(gridMember);
            if (CollectionUtils.isNotEmpty(gridIds)) {
                queryMap.put(GridConstant.GRID_IDS, gridIds);
            }
        }


        queryMap.put(NOT_NULL, NOT_NULL);
        int queryTotal = gridPlanTaskService.queryTotal(queryMap);
        vo.setEventReportCount(queryTotal);

        queryMap.remove(NOT_NULL);
        queryMap.put(LambdaUtil.getFieldName(GridPlanTaskDto::getExecStatus), GridPlanTaskConstant.TasksExecStatusEnum.STATUS_EXECUTED.getStatus());
        List<GridPlanTaskDto> gridPlanTaskDtos = gridPlanTaskService.queryList(queryMap);
        long totalSecond = 0L;
        for (GridPlanTaskDto task : gridPlanTaskDtos) {
            long betweenSecond = DateUtil.between(task.getExecStartTime(), task.getCompleteTime(), DateUnit.SECOND);
            totalSecond = totalSecond + betweenSecond;
        }
        vo.setUseTime(DateUtil.secondToTime(Math.toIntExact(totalSecond)));
        return Response.<GridPlanTaskStatisticsVo>ok().wrap(vo);

    }

    /**
     * 每日巡查任务数量统计
     * 总任务数量
     * 已完成数量
     * 未完成数量
     */
    @GetMapping("taskTrend")
    @Operation(summary = "每日巡查任务数量统计折线图")
    public Response<ByTimeCountResp> taskTrend(
            @Parameter(description = "时:HOUR 日：DAY 月：MONTH 默认按日 注:按时统计<=24h 按日统计<=14d 按月统计<=12m")
            @RequestParam(required = false, defaultValue = StatisticsConstants.TIME_RANGE_BY_MONTH) String type,
            @Parameter(description = "开始时间") @RequestParam String startTime,
            @Parameter(description = "结束时间") @RequestParam String endTime,
            @Parameter(description = "网格id") @RequestParam(required = false) Long gridId) {
        LoginGridMemberSession loginGridUserSession = SessionHolder.getLoginGridUserSession();
        assert loginGridUserSession != null;
        GridMemberDto gridMember = loginGridUserSession.getGridMember();

        Map<String, Object> params = new HashMap<>();

        // 没选中网格 那么传自己拥有的网格的
        if (gridId != null) {
            params.put(LambdaUtil.getFieldName(GridPlanTaskDto::getGridId), gridId);
        } else {
            // 网格员是否拥有网格信息
            List<Long> gridIds = resourcesBusiness.getGridIdByGridMember(gridMember);
            if (CollectionUtils.isNotEmpty(gridIds)) {
                params.put(GridConstant.GRID_IDS, gridIds);
            }
        }

        SqlConstants.MysqlDateTimeFormatTypeEnum typeEnum = SqlConstants.MysqlDateTimeFormatTypeEnum.DAY;
        Date startDate = null, endDate = null;
        String[] xData = null;
        switch (type) {
            case TIME_RANGE_BY_DAY:
                typeEnum = SqlConstants.MysqlDateTimeFormatTypeEnum.DAY;

                startDate = DateUtils.parse(startTime, DateUtils.FormatType.SIMPLE);
                endDate = DateUtils.parse(endTime, DateUtils.FormatType.SIMPLE);
                xData = DateUtils.timeSlotOfDay(startDate
                        , endDate, DateUtils.FormatType.SIMPLE, 1);

                break;
            case TIME_RANGE_BY_MONTH:
                typeEnum = SqlConstants.MysqlDateTimeFormatTypeEnum.MONTH;

                startDate = DateUtils.parse(startTime, DateUtils.FormatType.MONTH);
                endDate = DateUtils.parse(endTime, DateUtils.FormatType.MONTH);
                xData = DateUtils.timeSlotOfMonth(startDate
                        , endDate, DateUtils.FormatType.MONTH, 1);

                break;
            default:
                xData = new String[]{};
        }


        params.put(SqlConstants.DATE_FORMAT_TYPE_FILED, typeEnum.getFormat());
        List<TaskTrendStatisticsVo> resultList = gridPlanTaskService.countRecordGroupByTime(params);

        Map<String, TaskTrendStatisticsVo> taskTrendStatisticsVoMap = new HashMap<>();
        for (TaskTrendStatisticsVo workOrderCountDto : resultList) {
            taskTrendStatisticsVoMap.put(workOrderCountDto.getDateTime(), workOrderCountDto);
        }

        List<Integer> totalNums = new ArrayList<>();
        List<Integer> executedNums = new ArrayList<>();
        List<Integer> unexecutedNums = new ArrayList<>();
        for (String key : xData) {
            int untreatedCounter = 0, completedCounter = 0;

            TaskTrendStatisticsVo taskTrendStatisticsVo = taskTrendStatisticsVoMap.get(key);
            totalNums.add(taskTrendStatisticsVo == null ? 0 : taskTrendStatisticsVo.getTotalNum());
            executedNums.add(taskTrendStatisticsVo == null ? 0 : taskTrendStatisticsVo.getExecutedNum());
            unexecutedNums.add(taskTrendStatisticsVo == null ? 0 : taskTrendStatisticsVo.getUnexecutedNum());
        }

        ByTimeCountResp countResp = new ByTimeCountResp();
        countResp.setXData(xData);
        countResp.setYData(new ByTimeCountResp.DataInfo[]{
                new ByTimeCountResp.DataInfo("总任务数量", totalNums.stream().toArray(Integer[]::new)),
                new ByTimeCountResp.DataInfo("已完成数量", executedNums.stream().toArray(Integer[]::new)),
                new ByTimeCountResp.DataInfo("未完成数量", unexecutedNums.stream().toArray(Integer[]::new))
        });
        return Response.<ByTimeCountResp>ok().wrap(countResp);
    }

    @GetMapping("list")
    @Operation(summary = "网格巡检任务列表")
    public Response<Page<GridPlanTaskDto>> list(@Parameter(description = "巡检任务状态 1.当前可执行任务 2.历史任务") @RequestParam(required = false) String statusFlag,
                                                @Parameter(description = "从1开始的页码数") @RequestParam Integer pageNo,
                                                @Parameter(description = "每页多少条") @RequestParam Integer pageSize,
                                                @Parameter(description = "网格id") @RequestParam(required = false) Long gridId) {
        LoginGridMemberSession loginGridUserSession = SessionHolder.getLoginGridUserSession();
        GridMemberDto gridMember = loginGridUserSession.getGridMember();
        Request request = Request.configParams(Request.parseParams(httpServletRequest));

        // 没选中网格 那么传自己拥有的网格的
        if (gridId != null) {
            request.put(LambdaUtil.getFieldName(GridPlanTaskDto::getGridId), gridId);
        } else {
            // 网格员是否拥有网格信息
            List<Long> gridIds = resourcesBusiness.getGridIdByGridMember(gridMember);
            if (CollectionUtils.isNotEmpty(gridIds)) {
                request.put(GridConstant.GRID_IDS, gridIds);
            }
        }

        List<GridPlanTaskDto> result = null;
        int total = 0;

        // 当前可执行任务
        if (statusFlag.contains(GridPlanTaskConstant.IN_HAND)) {
            request.getParams().put(LambdaUtil.getFieldName(GridPlanTaskDto::getDate), DateUtil.format(new Date(), (DateUtils.FormatType.SIMPLE.getType())));
            request.getParams().put(LambdaUtil.getFieldName(GridPlanTaskDto::getExecStatus), SystemConstants.NO.toString());
        }

        // 结束时间小于当前时间 并且当前大于执行时间，代表已经执行了的
        if (statusFlag.contains(GridPlanTaskConstant.HISTORY)) {
            request.put(EXEC_END_TIME, EXEC_END_TIME);
        }
        total = gridPlanTaskService.queryTotal(request.getParams());
        if (total > 0) {
            result = gridPlanTaskService.queryList(request.getParams());
            for (GridPlanTaskDto taskDto : result) {
                if (DateUtil.isIn(new Date(), taskDto.getExecStartTime(), taskDto.getExecEndTime())) {
                    taskDto.setExecutableState(SystemConstants.YES);
                } else {
                    taskDto.setExecutableState(SystemConstants.NO);
                }
            }
        } else {
            result = new ArrayList<>();
        }

        Page<GridPlanTaskDto> page = new Page<>(request.getPageNo(), request.getPageSize(), total, result);
        return Response.<Page<GridPlanTaskDto>>ok().wrap(page);
    }

    /**
     * @param taskId
     * @return
     */
    @GetMapping("queryDetailByTaskId")
    @Operation(summary = "查看任务详情")
    public Response<GridPlanTaskDetailResp> queryByTaskId(@Parameter(description = "巡检任务id") @RequestParam Long taskId) {
        SessionHolder.checkGridMember();
        GridPlanTaskDto gridPlanTaskDto = gridPlanTaskService.selectCacheById(taskId);

        // 只返回给前端封装后的 resp对象
        GridPlanDto gridPlan
                = JacksonUtils.parse(gridPlanTaskDto.getPlanSnapshot(), GridPlanDto.class);
        GridPlanTaskDetailResp resp = new GridPlanTaskDetailResp();

        boolean in = DateUtil.isIn(new Date(), gridPlanTaskDto.getExecStartTime(), gridPlanTaskDto.getExecEndTime());
        if (in) {
            resp.setExecutableState(SystemConstants.YES);
        } else {
            resp.setExecutableState(SystemConstants.NO);
        }

        resp.setName(gridPlanTaskDto.getName());
        resp.setExecStartTime(gridPlanTaskDto.getExecStartTime());
        resp.setExecEndTime(gridPlanTaskDto.getExecEndTime());
        resp.setRevertPlanName(gridPlan.getName());
        resp.setDate(gridPlanTaskDto.getDate());
        resp.setCompleteTime(gridPlanTaskDto.getCompleteTime());
        resp.setGridName(gridPlanTaskDto.getGrid().getName());
        resp.setCreateTime(gridPlanTaskDto.getCreateTime());
        resp.setStatus(gridPlanTaskDto.getStatus());
        resp.setDetail(gridPlanTaskDto.getDetail());
        resp.setSnapshotImgs(gridPlanTaskDto.getSnapshotImgs());

        resp.setPatrolObj(gridPlan.getPatrolObj());
        resp.setPatrolObjAddress(gridPlan.getPatrolObjAddress());
        resp.setPatrolObjremark(gridPlan.getRemark());

        if (StringUtils.isNotEmpty(gridPlanTaskDto.getEventNo())) {
            GridEventRecordDto gridEventRecord
                    = gridEventRecordService.selectByEventNo(gridPlanTaskDto.getEventNo());
            if (gridEventRecord != null) {
                resp.setIsReport(GridPlanTaskConstant.ReportEventStatusEnum.REPORT.getStatus());
                resp.setEventName(gridEventRecord.getEventName());
                resp.setEventDesc(gridEventRecord.getEventDesc());
                resp.setEventTime(gridEventRecord.getEventTime());
                resp.setEventAddr(gridEventRecord.getEventAddr());
                resp.setEventPrimaryName(gridEventRecord.getEventPrimaryName());
                resp.setEventSecondaryName(gridEventRecord.getEventSecondaryName());
                resp.setEventLevel(gridEventRecord.getEventLevel());
                resp.setEventDetail(gridEventRecord.getEventDetail());
                resp.setEventPicturesJson(gridEventRecord.getEventPicturesJson());
                resp.setEventNo(gridEventRecord.getEventNo());
                resp.setEventDetail(gridEventRecord.getEventDetail());
                resp.setEventPicturesJson(gridEventRecord.getEventPicturesJson());
                resp.setSelfHandleResult(gridEventRecord.getSelfHandleResult());
                resp.setSelfHandlePicturesJson(gridEventRecord.getSelfHandlePicturesJson());
            }
        } else {
            resp.setIsReport(GridPlanTaskConstant.ReportEventStatusEnum.NO_REPORT.getStatus());
        }

        return Response.<GridPlanTaskDetailResp>ok().wrap(resp);
    }

    /**
     * 任务上报
     *
     * @param reportVo
     * @return
     */
    @PostMapping("report")
    @Operation(summary = "上报记录")
    public Response<Object> report(@RequestBody GridPlanTaskVo reportVo) {
        LoginGridMemberSession loginGridUserSession = SessionHolder.getLoginGridUserSession();

        assert loginGridUserSession != null;
        GridMemberDto gridMember = loginGridUserSession.getGridMember();
        Long taskId = reportVo.getTaskId();
        String eventCode = null;

        // 判断点位状态是否已经执行过  taskRouteStatus 和 未执行不一致 直接reture
        GridPlanTaskDto gridPlanTaskDto = gridPlanTaskService.selectById(taskId);

        if (gridPlanTaskDto.getStatus() != null) {
            return Response.error("当前点位已上报无需重复上报");
        }

        if (!RedisUtils.setnx(GRID_TASK_REPORT_LIMIT_LOCK_BY_TASKID + taskId, SystemConstants.YES.toString(), GRID_TASK_REPORT_TIME_LIMIT, TimeUnit.SECONDS)) {
            return Response.error("上报过于频繁");
        }

        //参数校验
        if (GridPlanTaskConstant.TasksExecStatusEnum.STATUS_EXECUTED.getStatus().equals(gridPlanTaskDto.getExecStatus())) {
            return Response.error("当前任务不可执行");
        }

        GridDto gridDto = gridService.selectById(gridPlanTaskDto.getGridId());
        if (gridDto == null) {
            return Response.error("网格不存在");
        }

        //校验是否在执行时间
        Date now = new Date();
        String dateStr = new SimpleDateFormat("yyyy-MM-dd").format(now);
        String time = new SimpleDateFormat("HH:mm").format(now);
        if (!Objects.equals(dateStr, gridPlanTaskDto.getDate()) || time.compareTo(gridPlanTaskDto.getPeriod().split("-")[0]) < 0 || time.compareTo(gridPlanTaskDto.getPeriod().split("-")[1]) > 0) {
            return Response.error("当前时间不在该任务的时间段范围之内");
        }

        // 处理拼装的数据
        GridPlanTaskDto updately = new GridPlanTaskDto();
        updately.setId(taskId);
        updately.setStatus(reportVo.getStatus());
        updately.setExecUserId(gridMember.getId());
        updately.setExecUsername(gridMember.getName());
        updately.setExecStatus(GridPlanTaskConstant.TasksExecStatusEnum.STATUS_EXECUTED.getStatus());
        updately.setCompleteTime(now);
        updately.setDetail(reportVo.getDetail());
        updately.setSnapshotImgs(reportVo.getSnapshotImgs());
        // 选择事件上报 生成事件编号
        if (reportVo.getAssignWorkOrder().equals(GridPlanTaskConstant.ReportEventStatusEnum.REPORT.getStatus())) {
            eventCode = OrderUtils.getOrderNo(OrderCodePrefixConstants.GRID_EVENT_RECORD_PREFIX);
            updately.setEventNo(eventCode);

            // 拼装reportEvent 参数
            GridEventRecordDto gridEventRecord = new GridEventRecordDto();
            gridEventRecord.setEventPrimaryId(reportVo.getEventPrimaryId());
            gridEventRecord.setEventSecondaryId(reportVo.getEventSecondaryId());
            gridEventRecord.setEventLevel(reportVo.getEventLevel());
            gridEventRecord.setEventDesc(reportVo.getEventDesc());
            gridEventRecord.setEventDetail(reportVo.getEventDesc());
            gridEventRecord.setEventPicturesJson(reportVo.getEventPicturesJson());
            gridEventRecord.setGridId(gridDto.getId());
            gridEventRecord.setEventName(reportVo.getEventName());
            gridEventRecord.setEventAddr(reportVo.getEventAddr());
            gridEventRecord.setEventTime(new Date());

            if (reportVo.getEventType().equals(GridPlanTaskConstant.ReportEventTypeEnum.OWN.getStatus())) {
                gridEventRecord.setSelfHandleResult(reportVo.getSelfHandleResult());
                gridEventRecord.setSelfHandlePicturesJson(reportVo.getEventPicturesJson());
                gridEventRecord.setEventType(GridPlanTaskConstant.ReportEventTypeEnum.OWN.getStatus());
            } else {
                gridEventRecord.setEventType(GridPlanTaskConstant.ReportEventTypeEnum.SUPERIORS.getStatus());
            }

            //生成上报事件
            GridEventRecordDto gridEventRecordDto = gridEventRecordBusiness.genReportEvent(gridEventRecord, gridDto, gridMember);
            //覆盖eventNo
            gridEventRecordDto.setEventNo(eventCode);
            gridEventRecordService.saveOrUpdateWithLog(gridEventRecordDto);

        }

        // 更新任务状态
        gridPlanTaskService.updateById(updately);

        return Response.<Object>ok().wrap("提交成功");
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("https://dev-smartcity.quectel.com:10443/public-bucket/2021/11/25/74f2a7af7b6342a3be20dcdb66fa6dd6.jpeg");
        String snapshotImg = list.toString();
        if (snapshotImg != null) {
            JacksonUtils.parseArray(snapshotImg, String.class);
        }

    }
}
